package cn.geodata.tools;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.configuration.XMLConfiguration;
import org.geotools.data.FeatureStore;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.feature.AttributeType;
import org.geotools.feature.Feature;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.FeatureType;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.Envelope2D;

import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

public class TileRetrieve {
	private static Logger log = Logger.getAnonymousLogger();
	
	private List<String> layers;
	private Map<String, RasterManager> rasters;
	private XMLConfiguration conf;
	
	public static void main(String[] args) throws IOException {
		TileRetrieve _r = new TileRetrieve();
		_r.retriveTile();
	}
	
	public TileRetrieve() throws IOException {
		conf = Configure.getConfig();
		
		this.layers = Arrays.asList(conf.getStringArray("layers"));
		this.rasters = new HashMap<String, RasterManager>();
		for(String _layer : this.layers){
			log.info("load layer " + _layer);
			this.rasters.put(_layer, new RasterManager(new File(conf.getString(_layer + ".path")), conf.getInt(_layer + ".nodata", 0)));
		}
	}
	
	public List<String> getLayers(){
		return this.layers;
	}
	
	public void retriveTile() throws IOException {
		for(String _layer: this.layers){
			this.retriveTile(_layer);
		}
	}
	
	public void retriveTile(String layer) throws IOException{
		log.info("retrieve layer " + layer);
		FeatureIterator _it = (new TileManager()).getTiles().features();
		File _output = new File(this.conf.getString(layer + ".output"));
		try{
			while(_it.hasNext()){
				Feature _f = _it.next();
				MultiPolygon _multiPoly = (MultiPolygon) _f.getDefaultGeometry();
				Polygon _poly = (Polygon) _multiPoly.getGeometryN(0);
				Polygon _buffer = (Polygon) _poly.buffer(Configure.getConfig().getDouble("distance"));

				int _path = (Integer)_f.getAttribute("PATH");
				int _row = (Integer)_f.getAttribute("ROW");
				
				this.retriveTile(layer, _path, _row, _buffer, _output);
			}
		}
		finally{
			_it.close();
		}
	}
	
	protected void outputPolygon(Polygon poly) {
		try{
			FeatureType _featureType = CommonFactoryFinder.getFeatureTypeFactory(GeoTools.getDefaultHints()).newFeatureType(
					new AttributeType[] {
						CommonFactoryFinder.getAttributeTypeFactory(GeoTools.getDefaultHints()).newAttributeType("shape", Polygon.class)
					}, "output.shp");
			
			
			ShapefileDataStore _dataStore = new ShapefileDataStore(new File("H:\\temp\\output.shp").toURL());
			_dataStore.createSchema(_featureType);
			
			FeatureStore _store = (FeatureStore) _dataStore.getFeatureSource();
			
			FeatureCollection _fs = CommonFactoryFinder.getFeatureCollections(GeoTools.getDefaultHints()).newCollection();
			_fs.add(_featureType.create(new Object[] {poly}));
			
			_store.addFeatures(_fs );
		}
		catch(Exception e){
			log.log(Level.SEVERE, "Failed to output feature", e);
		}
	}
	
	public EnviRasterGrid retriveTile(String layer, int path, int row, Polygon tile, File output) throws IOException{
		RasterManager _raster = this.rasters.get(layer);
		
		List<RasterInfo> _list = _raster.findDatasets(tile);
		if(_list.size() == 0){
			log.info("\not found dataset for layer " + layer + " path=" + path + " row=" + row);			
			return null;
		}
		
		log.info("\tretrieve layer " + layer + " path=" + path + " row=" + row);

		
		Envelope2D _env = Utilities.getEnvelope(tile);
		double _cellSize = conf.getDouble(layer + ".cell");
		
		int _cols = (int) Math.ceil(_env.width / _cellSize);
		int _rows = (int) Math.ceil(_env.height / _cellSize);
		
		Envelope2D _env2 = new Envelope2D(
				new DirectPosition2D(_env.getMinX(), _env.getMaxY() - _cellSize * _rows),
				new DirectPosition2D(_env.getMinX() + _cellSize * _cols, _env.getMaxY())
				);
		
		int _dataType = conf.getInt(layer + ".datatype", 2);
		log.info("data type:" + _dataType);
		
		EnviRasterGrid _grid = new EnviRasterGrid(layer, _dataType, path, row, _env2, _rows, _cols, _cellSize, output);
		
//		this.outputPolygon(tile);
		_grid.writeHeader();
		
		try{
			int _count = 0;
			int _percent = _rows / 100;
			
			_grid.start();
			
			double _y = _env.getMaxY() - _cellSize / 2;
			for (int _row = 0; _row < _rows; _row++) {
				if(_count == 0){
					System.out.print("\r" + _row * 100 / _rows + "%");
//					log.info("\t" + _row * 100 / _rows + "%...");
				}
				
				double _x = _env.getMinX() + _cellSize / 2;
				for (int _col = 0; _col < _cols; _col++) {
					_grid.outputEnvi(_raster.getLocationValue(_list, _x, _y));
					_x += _cellSize;
				}
				_y -= _cellSize;
				
				_count ++;
				if(_count > _percent){
					_count = 0;
				}
			}
			System.out.println("\r100%% finished");
//			log.info("\t100% finished");
			return _grid;
		}
		finally{
			_grid.close();
		}
	}
}
